home *** CD-ROM | disk | FTP | other *** search
- /*
- * FRAME.C A general-purpose bitmap creating library. It allows you
- * to select a portion of any screen by dragging a selection
- * rectangle, and returns a bitmap structure that contains
- * a copy of the selected region.
- *
- * Copyright 1990 by Davide P. Cervone, all rights reserved.
- * You may use this code, provided this copyright notice is kept intact.
- */
-
- #define INTUITIONPRIVATE TRUE
- #include <intuition/intuitionbase.h>
- #include <exec/interrupts.h>
- #include <exec/memory.h>
- #include <devices/input.h>
- #include <devices/inputevent.h>
- #include <libraries/dos.h>
-
- #include "Frame.h"
-
- struct LayersBase *LayersBase;
-
- struct Screen *theScreen; /* the selected screen */
- UBYTE PlaneMask = 0xFF; /* which bitplanes to use */
- UBYTE theDepth; /* the depth of the bitmap */
- WORD theWidth, theHeight; /* its width and height */
- ULONG theModes; /* a copy of the screen's modes */
- struct ColorMap *theColorMap; /* a copy of the screen's color map */
- struct RastPort *theRastPort; /* the screen's RastPort */
- struct BitMap theBitMap; /* the copied bitmap */
- WORD StartX, StartY; /* position within the screen */
- WORD CurrentX, CurrentY; /* current mouse position */
- WORD LeftX, TopY; /* corner of selection rectangle */
-
-
- extern struct MsgPort *CreatePort();
- extern struct IOStdReq *CreateStdIO();
- extern LONG AllocSignal(), Wait(), SetSignal();
- extern APTR FindTask();
- extern PLANEPTR AllocRaster();
- extern struct ColorMap *GetColorMap();
- extern APTR AllocMem();
-
- extern void FrameHandlerStub();
-
-
- #define INTUITION_REV 0L
- #define LAYERS_REV 0L
- #define GRAPHICS_REV 0L
-
- #define EXIT_OK 0L
- #define EXIT_ERROR 10L
-
- #define STARTQUAL (IEQUALIFIER_LSHIFT | IEQUALIFIER_RSHIFT)
-
- #define NewX(i) (Frame->XY[i*2])
- #define NewY(i) (Frame->XY[i*2+1])
- #define OldX(i) (Frame->NextBorder->XY[i*2])
- #define OldY(i) (Frame->NextBorder->XY[i*2+1])
- #define SCREENX(x) (((x) >> Xshift) - theScreen->LeftEdge)
- #define SCREENY(y) (((y) >> Yshift) - theScreen->TopEdge)
- #define INTUIX(x) ((x) << Xshift)
- #define INTUIY(y) ((y) << Yshift)
- #define SCREENTOP\
- (theScreen->TopEdge << ((theScreen->ViewPort.Modes & LACE)? 0: 1))
- #define CTRL_C(e)\
- ((e)->ie_Code == 0x33 && ((e)->ie_Qualifier & IEQUALIFIER_CONTROL))
-
- #define SIGBREAKF_ANY\
- (SIGBREAKF_CTRL_C| SIGBREAKF_CTRL_D| SIGBREAKF_CTRL_E| SIGBREAKF_CTRL_F)
-
- #define WIDTH ((long)theWidth)
- #define HEIGHT ((long)theHeight)
- #define DEPTH ((long)theDepth)
- #define BLT_COPY 0x000000C0
- #define BLT_ALLPLANES 0x000000FF
-
- struct MsgPort *InputPort = NULL; /* Port used to talk to Input.Device */
- struct IOStdReq *InputBlock = NULL; /* request block used with Input.Device */
- static short InputOpen = FALSE; /* flag for when input.Device is open */
- static APTR theTask; /* pointer to our task */
- static LONG OkSignal; /* signal when movement detected */
- static LONG EndSignal; /* signal when frame is done */
- static LONG OkMask; /* 1 << OkSignal */
- static LONG EndMask; /* 1 << EndSignal */
-
- static short OkToSignal = TRUE; /* TRUE if not using shared memory */
- static short MouseMoved = FALSE; /* TRUE when mouse moves buffered */
- static short RemoveEvent; /* should an input event be removed? */
- static short Xshift, Yshift; /* HIRES and LACE adjustments */
- static WORD Xs,Ys, Xc, Yc; /* starting and current X,Y */
- static WORD Hmin,Hmax, Wmin, Wmax; /* min and max movement values */
-
- static SHORT NewBox[5][2]; /* current select frame */
- static SHORT OldBox[5][2]; /* previous select frame */
-
- struct Border NewFrame = {0,0, 0x01,0, COMPLEMENT, 5, &NewBox[0][0], NULL};
- struct Border OldFrame = {0,0, 0x01,0, COMPLEMENT, 5, &OldBox[0][0], &NewFrame};
- struct Border *Frame = &OldFrame;
-
- static struct Interrupt HandlerData = /* used to add an input handler */
- {
- {NULL, NULL, 0, 52, NULL}, /* Node structure (nl_Pri = 52) */
- NULL, /* data pointer */
- &FrameHandlerStub /* code pointer */
- };
-
-
- /*
- * GetMinMax()
- *
- * If a screen is selected,
- * Get the min and max values from the screen
- * Otherwise get them from Intuition itself
- */
-
- static void GetMinMax()
- {
- if (theScreen)
- {
- Hmin = INTUIY(theScreen->TopEdge);
- Hmax = INTUIY(theScreen->Height + theScreen->TopEdge - 1);
- if (Hmax > IntuitionBase->MaxYMouse) Hmax = IntuitionBase->MaxYMouse;
- Wmin = INTUIX(theScreen->LeftEdge);
- Wmax = INTUIX(theScreen->Width + theScreen->LeftEdge - 1);
- if (Wmax > IntuitionBase->MaxXMouse) Wmax = IntuitionBase->MaxXMouse;
- } else {
- Hmin = IntuitionBase->MinYMouse;
- Hmax = IntuitionBase->MaxYMouse;
- Wmin = IntuitionBase->MinXMouse;
- Wmax = IntuitionBase->MaxXMouse;
- }
- }
-
-
- /*
- * DoSelectDown()
- *
- * Remove the event.
- * Starting at the first screen find the first one higher than the mouse
- * If none, choose the active screen, otherwise bring the screen to the front.
- * Get the shift values from the screen's modes, and save the current X,Y
- */
-
- static void DoSelectDown()
- {
- RemoveEvent = TRUE;
- theScreen = IntuitionBase->FirstScreen;
- while (theScreen && IntuitionBase->MouseY < SCREENTOP)
- theScreen = theScreen->NextScreen;
- if (theScreen == NULL)
- theScreen = IntuitionBase->ActiveScreen;
- else
- ScreenToFront(theScreen);
- Xshift = (theScreen->ViewPort.Modes & HIRES)? 0: 1;
- Yshift = (theScreen->ViewPort.Modes & LACE)? 0: 1;
- Xs = Xc;
- Ys = Yc;
- }
-
-
- /*
- * DoMouseMove()
- *
- * Get the mouse's movement and clip it to the movement rectangle.
- * If right button is presseed and the select rectangle is started,
- * Adjust the mouse movement, if necessary
- * Move the starting position by this amount (ie, move the frame)
- * If there was some movement,
- * Don't remove the event,
- * Pass on the modified mouse movements
- * Adjust the mouse's current position
- * If the select rectangle is already started, indicate that it has changed.
- */
-
- static void DoMouseMove(theEvent)
- struct InputEvent *theEvent;
- {
- register WORD dx,dy;
-
- dx = theEvent->ie_X;
- dy = theEvent->ie_Y;
- if (dx < Wmin - Xc) dx = Wmin - Xc;
- if (dy < Hmin - Yc) dy = Hmin - Yc;
- if (dx > Wmax - Xc) dx = Wmax - Xc;
- if (dy > Hmax - Yc) dy = Hmax - Yc;
- if ((theEvent->ie_Qualifier & IEQUALIFIER_RBUTTON) && theScreen)
- {
- if (dx < Wmin - Xs) dx = Wmin - Xs;
- if (dy < Hmin - Ys) dy = Hmin - Ys;
- if (dx > Wmax - Xs) dx = Wmax - Xs;
- if (dy > Hmax - Ys) dy = Hmax - Ys;
- Xs += dx; Ys += dy;
- }
- if (dx != 0 || dy != 0)
- {
- RemoveEvent = FALSE;
- theEvent->ie_X = dx;
- theEvent->ie_Y = dy;
- Xc += dx;
- Yc += dy;
- if (theScreen) MouseMoved = TRUE;
- }
- }
-
-
- /*
- * SignalMove()
- *
- * Mark that the shared memory is in use
- * Save the start and current X,Y in the shared memory
- * Signal the main task to draw the select rectangle
- *
- * (This stuff should all be replaced by message passing - I wrote this
- * a long time ago before I had much experience)
- */
-
- static void SignalMove()
- {
- OkToSignal = FALSE;
- MouseMoved = FALSE;
- StartX = SCREENX(Xs);
- StartY = SCREENY(Ys);
- CurrentX = SCREENX(Xc);
- CurrentY = SCREENY(Yc);
- Signal(theTask,OkMask);
- }
-
-
- /*
- * FrameHandler()
- *
- * Get the current mouse position, and get the mouse movement rectangle.
- * While there are more events to look at,
- * Keep the event unless we say otherwise,
- * Check the type of event:
- * RAWMOUSE:
- * Remove the event if the select box is up
- * Check the button type:
- * SELECTDOWN:
- * If the select box is not already going, and the right qualifiers
- * are present, then start the box
- * SELECTUP:
- * If the select box is going, signal that it's done
- * MENU:
- * Ignore these
- * Anything else (i.e., MOUSEMOVE):
- * Do a mouse move
- * RAWKEY:
- * If it is a CTRL-C, signal the task to stop.
- * If we are removing the event, do so, otherwise go on to the next one.
- * If mouse moves took placem and the memory is not in use, signal the move.
- * Return the (modified) event list.
- */
-
- struct InputEvent *FrameHandler(EventList,data)
- struct InputEvent *EventList;
- APTR data;
- {
- register struct InputEvent **EventPtr = &EventList;
- register struct InputEvent *theEvent;
-
- Xc = IntuitionBase->MouseX;
- Yc = IntuitionBase->MouseY;
- GetMinMax();
-
- Forbid();
- while ((theEvent = *EventPtr) != NULL)
- {
- RemoveEvent = FALSE;
- switch(theEvent->ie_Class)
- {
- case IECLASS_RAWMOUSE:
- RemoveEvent = (theScreen != NULL);
- switch(theEvent->ie_Code)
- {
- case SELECTDOWN:
- if (theScreen == NULL &&
- (theEvent->ie_Qualifier & STARTQUAL))
- DoSelectDown();
- break;
-
- case SELECTUP:
- if (theScreen) Signal(theTask,EndMask);
- break;
-
- case MENUDOWN:
- case MENUUP:
- break;
-
- default:
- DoMouseMove(theEvent);
- break;
- }
- break;
-
- case IECLASS_RAWKEY:
- if (CTRL_C(theEvent)) Signal(theTask,SIGBREAKF_CTRL_C);
- break;
- }
- if (RemoveEvent)
- *EventPtr = theEvent->ie_NextEvent;
- else
- EventPtr = &(theEvent->ie_NextEvent);
- }
- Permit();
- if (OkToSignal && MouseMoved) SignalMove();
- return(EventList);
- }
-
-
- /*
- * Ctrl_C()
- *
- * Dummy routine to disable Lattice-C CTRL-C trapping.
- */
-
- #ifndef MANX
- static int Ctrl_C()
- {
- return(0);
- }
- #endif
-
-
- /*
- * DoExit()
- *
- * General purpose exit routine. If 's' is not NULL, then print an
- * error message with up to three parameters. Free any memory, close
- * any open files, delete any ports, free any used signals, etc.
- */
-
- void DoExit(s,x1,x2,x3)
- char *s, *x1, *x2, *x3;
- {
- long status = EXIT_OK;
- short i;
- extern void DoUseExit();
-
- if (theRastPort) UnlockLayers(&theScreen->LayerInfo);
- if (s != NULL)
- {
- printf(s,x1,x2,x3);
- printf("\n");
- status = EXIT_ERROR;
- }
- DoUseExit();
- for (i=0; i<theDepth; i++)
- {
- if (theBitMap.Planes[i])
- FreeRaster(theBitMap.Planes[i],theWidth,theHeight);
- }
- if (InputOpen) CloseDevice(InputBlock);
- if (InputBlock) DeleteStdIO(InputBlock);
- if (InputPort) DeletePort(InputPort);
- if (theColorMap) FreeColorMap(theColorMap);
- if (OkSignal) FreeSignal(OkSignal);
- if (EndSignal) FreeSignal(EndSignal);
- if (IntuitionBase) CloseLibrary(IntuitionBase);
- if (LayersBase) CloseLibrary(LayersBase);
- if (GfxBase) CloseLibrary(GfxBase);
- exit(status);
- }
-
-
- /*
- * New()
- *
- * Get a chunk of memory and check that it has been allocated OK.
- * If not, exit with an error message.
- */
-
- APTR New(name,size)
- char *name;
- int size;
- {
- APTR ptr;
-
- if ((ptr = AllocMem(size,MEMF_CLEAR | MEMF_PUBLIC)) == NULL)
- DoExit("Can't Allocate Memory for %s",name);
- return(ptr);
- }
-
-
- /*
- * CheckLibOpen()
- *
- * General library open routine. It opens a library and sets a pointer
- * to it. It checks that the library was openned successfully.
- */
-
- void CheckLibOpen(lib,name,rev)
- APTR *lib;
- char *name;
- int rev;
- {
- extern APTR OpenLibrary();
-
- if ((*lib = OpenLibrary(name,(LONG)rev)) == NULL)
- DoExit("Can't open %s",name);
- }
-
-
- /*
- * GetSignal()
- *
- * Allocate a signal (error if none available) and set the mask to
- * the proper value.
- */
-
- static void GetSignal(theSignal,theMask)
- LONG *theSignal, *theMask;
- {
- LONG signal;
-
- if ((signal = AllocSignal(-ONE)) == -ONE) DoExit("Can't Get Signal");
- *theSignal = signal;
- *theMask = (ONE << signal);
- }
-
-
- /*
- * SetupTask()
- *
- * Find the task pointer for the main task (so the input handler can
- * signal it). Clear the CTRL signal flags (so we don't get any left
- * over from before JOURNAL was run) and allocate some signals for
- * new events and errors (so the input handler can signal them).
- */
-
- static void SetupTask()
- {
- theTask = FindTask(NULL);
- SetSignal(0L,SIGBREAKF_ANY);
- GetSignal(&OkSignal,&OkMask);
- GetSignal(&EndSignal,&EndMask);
- #ifndef MANX
- onbreak(&Ctrl_C);
- #endif
- }
-
-
- /*
- * TellInputDevice()
- *
- * Create a port and a standard IO block for talking to the input device.
- * Open the input device and tell it to add or remove the input handler.
- * The handler is at priority 51, so it will be ahead of Intuition.
- * Once the handler is added or removed, close the device and delete the
- * request block and port.
- */
-
- static void TellInputDevice(function)
- long function;
- {
- long status;
-
- if ((InputPort = CreatePort(0,0)) == NULL)
- DoExit("Can't Create Port for Input Device");
- if ((InputBlock = CreateStdIO(InputPort)) == NULL)
- DoExit("Can't Create IO Block for Input Device");
- if (OpenDevice("input.device",0,InputBlock,0) != 0)
- DoExit("Can't Open Input Device");
- InputOpen = TRUE;
-
- InputBlock->io_Command = function;
- InputBlock->io_Data = (APTR) &HandlerData;
- if (status = DoIO(InputBlock)) DoExit("Error from DoIO: %ld",status);
-
- CloseDevice(InputBlock); InputOpen = FALSE;
- DeleteStdIO(InputBlock); InputBlock = NULL;
- DeletePort(InputPort); InputPort = NULL;
- }
-
-
- /*
- * DrawFrame()
- *
- * Set the corners of the border to their new positions.
- * If the select frame has been moved,
- * Get the old X,Y and calculate the new X,Y for the intermediate
- * part of the border (the border is not square so as to avoid
- * unnecessary flickering when it is sized)
- * Update the intermediate border positions
- * Otherwise
- * Update the intermediate positions
- & Draw the borders
- * Switch which one is the current one, and link them properly
- */
-
- static void DrawFrame()
- {
- WORD Xo,Yo, Xn,Yn;
-
- NewX(3) = NewX(4) = StartX; NewX(1) = NewX(2) = CurrentX;
- NewY(0) = NewY(1) = StartY; NewY(2) = NewY(3) = CurrentY;
- if (StartX == OldX(4) && StartY == OldY(0))
- {
- Xo = OldX(2); Yo = OldY(2);
- if ((CurrentX > StartX && StartX >= Xo) ||
- (CurrentX < StartX && StartX <= Xo))
- {
- Xn = StartX;
- } else {
- if ((CurrentX > Xo && Xo > StartX) ||
- (CurrentX < Xo && Xo < StartX))
- Xn = Xo;
- else
- Xn = CurrentX;
- }
- if ((CurrentY > StartY && StartY >= Yo) ||
- (CurrentY < StartY && StartY <= Yo))
- {
- Yn = StartY;
- } else {
- if ((CurrentY > Yo && Yo > StartY) ||
- (CurrentY < Yo && Yo < StartY))
- Yn = Yo;
- else
- Yn = CurrentY;
- }
- OldX(0) = NewX(0) = Xn;
- OldY(4) = NewY(4) = Yn;
- } else {
- OldX(0) = OldX(4); OldY(4) = OldY(0);
- NewX(0) = StartX; NewY(4) = StartY;
- }
- DrawBorder(theRastPort,Frame,0L,0L);
- Frame->NextBorder->NextBorder = Frame;
- Frame = Frame->NextBorder;
- Frame->NextBorder->NextBorder = NULL;
- }
-
-
- /*
- * CopyFrame()
- *
- * Get the mask for the planes actually needed
- * Count the number of planes involved
- * If none, give an error
- * Get the widthm height, and upper-left-hand corner of the rectangle.
- * Get a color map of the required depth and copy the screen's
- * color map into it, skipping unneeded colors
- * Get the screen's modes
- * Initialize the BitMap, and a temporary one (to be used for blitting)
- * For each plane selected
- * Allocate a raster area for it (error if it not possible)
- * Use it as the bitplane for both the TempBitMap and the BitMap
- * (BitMap has them sequentially, TempBitMap has them in the positions
- * corresponding to their positions in the screen bitmap)
- * Clear the last two bytes of each bitplane (to make sure they are 0)
- * Copy the screen data to the TempBitMap (and hence the to BitMap, since
- * they share bitplanes)
- */
-
- void CopyFrame()
- {
- short i,j,b;
- UWORD *OldC,*NewC;
- PLANEPTR thePlane;
- UBYTE TempMask, NotPlaneMask;
- struct BitMap TempBitMap;
- long TempDepth = theScreen->BitMap.Depth;
-
- PlaneMask &= (1 << TempDepth) - 1;
- NotPlaneMask = ~PlaneMask;
- for (TempMask = PlaneMask, theDepth = 0; TempMask; TempMask >>= 1)
- theDepth += (TempMask & 1);
- if (theDepth == 0) DoExit("No Bit Planes Selected By Mask");
-
- if (CurrentX > StartX)
- {
- theWidth = CurrentX - StartX + 1;
- LeftX = StartX;
- } else {
- theWidth = StartX - CurrentX + 1;
- LeftX = CurrentX;
- }
- if (CurrentY > StartY)
- {
- theHeight = CurrentY - StartY + 1;
- TopY = StartY;
- } else {
- theHeight = StartY - CurrentY + 1;
- TopY = CurrentY;
- }
-
- theColorMap = GetColorMap((long)(1 << theDepth));
- if (theColorMap == 0) DoExit("Can't Get Color Map");
- OldC = (UWORD *)(theScreen->ViewPort.ColorMap->ColorTable);
- NewC = (UWORD *)(theColorMap->ColorTable);
- for (i=0; i<theScreen->ViewPort.ColorMap->Count; i++, OldC++)
- if ((i & NotPlaneMask) == 0) *NewC++ = *OldC;
-
- theModes = theScreen->ViewPort.Modes;
-
- InitBitMap(&theBitMap,DEPTH,WIDTH,HEIGHT);
- InitBitMap(&TempBitMap,TempDepth,WIDTH,HEIGHT);
- for (TempMask=PlaneMask, i=j=0; TempMask; TempMask >>= 1,j++)
- {
- if (TempMask & 1)
- {
- thePlane = AllocRaster(WIDTH,HEIGHT);
- if (thePlane == NULL) DoExit("Can't Get BitPlane %d",i);
- TempBitMap.Planes[j] = theBitMap.Planes[i++] = thePlane;
- for (b=RASSIZE(theWidth,theHeight)-1; b>0; b -= theBitMap.BytesPerRow)
- thePlane[b] = thePlane[b-1] = 0;
- }
- }
- BltBitMap(&theScreen->BitMap,(long)LeftX,(long)TopY,&TempBitMap,0L,0L,
- WIDTH,HEIGHT,BLT_COPY,(long)PlaneMask,NULL);
- }
-
-
- /*
- * TrackFrame()
- *
- * Setup the task, and get the signal mask
- * Add the input handler and give the installation message
- * Tell the user how to get the select rectangle
- * While there is more to do,
- * Wait for something to happen
- * If we have gotten a new mouse position,
- * If the screen is already chosen,
- * Update and draw the new frame
- * Otherwise,
- * Get the rastport of the selected screen,
- * Lock its layers so nothing happens while we're selecting
- * Initialize the frames and draw them on the screen
- * Indicate that we are done with the shared memory.
- * If some other signal is present,
- * End the loop
- * Check if the loop was cancelled by the user
- * If the select box was drawn,
- * Finish the frame and remove it
- * If not cancelled, get a copy of the frame
- * Unlock the screen layers so things can happen again
- * Forget the rastport
- * Remove the handler
- * return the status
- */
-
- static int TrackFrame()
- {
- LONG signals;
- LONG SigMask;
- short NotDone = TRUE;
- int NotCancelled = TRUE;
-
- SetupTask();
- SigMask = OkMask | EndMask | SIGBREAKF_CTRL_C;
-
- TellInputDevice(IND_ADDHANDLER);
- printf("%s %s Installed\n",program,version);
- printf("Drag a selection rectangle while holding the SHIFT key. To change\n");
- printf("the position of the box, press the right mouse button while dragging.\n");
- printf("Press CTRL-C to cancel at any time.\n");
- while (NotDone)
- {
- signals = Wait(SigMask);
- if (signals & OkMask)
- {
- if (theRastPort)
- {
- DrawFrame();
- } else {
- theRastPort = &theScreen->RastPort;
- LockLayers(&theScreen->LayerInfo);
- OldX(0) = OldX(3) = OldX(4) = StartX; OldX(1) = OldX(2) = CurrentX;
- OldY(0) = OldY(1) = OldY(4) = StartY; OldY(2) = OldY(3) = CurrentY;
- DrawBorder(theRastPort,Frame->NextBorder,0L,0L);
- }
- OkToSignal = TRUE;
- }
- if (signals & ~OkMask)
- {
- NotDone = FALSE;
- NotCancelled = ((signals & SIGBREAKF_CTRL_C) == 0);
- if (theRastPort)
- {
- OldX(0) = OldX(4);
- OldY(4) = OldY(0);
- DrawBorder(theRastPort,Frame->NextBorder,0L,0L);
- if (NotCancelled) CopyFrame();
- UnlockLayers(&theScreen->LayerInfo);
- theRastPort = NULL;
- }
- }
- }
- TellInputDevice(IND_REMHANDLER);
- return(NotCancelled);
- }
-
-
- /*
- * main()
- *
- * Parse the command-line arguments and perform the proper function
- * (either show the usage, exit, or get a section of bitmap and use it).
- */
-
- void main(argc,argv)
- int argc;
- char **argv;
- {
- int function;
- extern int ParseArguments();
- extern void UseFrame();
-
- function = ParseArguments(argc,argv);
- switch(function)
- {
- case SHOW_USAGE:
- printf("Usage: %s\n",usage);
- break;
-
- case JUST_EXIT:
- break;
-
- default:
- CheckLibOpen(&IntuitionBase,"intuition.library",INTUITION_REV);
- CheckLibOpen(&LayersBase,"layers.library",LAYERS_REV);
- CheckLibOpen(&GfxBase,"graphics.library",GRAPHICS_REV);
- if (TrackFrame()) UseFrame(function);
- break;
- }
- DoExit(NULL);
- }
-